package ub.pis;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import ub.pis.net.Goear;
import ub.pis.net.RelatedArtistParser;
import ub.pis.net.TopSongsParser;

import android.app.NotificationManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.net.Uri;

import android.os.DeadObjectException;
import android.os.IBinder;
import android.os.RemoteException;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;

public class MediaService extends Service implements Runnable {
	private NotificationManager nm;
	private static final int NOTIFY_ID = R.layout.pant4;
	private ArrayList<Canco> cancons = new ArrayList<Canco>();
	private ArrayList<Canco> back = new ArrayList<Canco>();
	private boolean truca;
	private boolean restart;
	private MediaPlayer mp = new MediaPlayer();
	private Canco actual;
	private Random random;
	private int randint;
	
	private BDArtista db;

	//Control de llamada.
	private TelephonyManager telephonyManager;
	private PhoneStateListener listener;
	
	private volatile Thread runner;

	
	@Override
	public void onCreate() {
		nm = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
		db = new BDArtista(this);
		db.open();
		random = new Random();
		
		actual = new Canco();
		telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
		listener = new PhoneStateListener() {
			public void onCallStateChanged(int state,String incomingNumber){
				String stateString = "N/A";
				switch(state){
					case TelephonyManager.CALL_STATE_IDLE:
						if(truca){
							restart = mp.isPlaying();
							mp.pause();
						} else {
							if(restart){
								mp.start();
							}
						}
						truca = false;
						stateString = "idle";
						break;
					case TelephonyManager.CALL_STATE_OFFHOOK:
						stateString = "Off Hook";
						break;
					case TelephonyManager.CALL_STATE_RINGING:
						stateString = "Ringing";
						restart = mp.isPlaying();
						mp.pause();
						truca = true;
						break;
				}
			}
		};
		startThread();
		try {
			playSong("None",false);
		} catch (RemoteException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
	}
	
	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		return START_STICKY;
	}
	
	@Override
	public void onDestroy() {
		stopThread(); //Paramos el thread
		mp.stop(); //Paramos la reproduccion
		mp.release();
		nm.cancel(NOTIFY_ID);
		db.close(); // Cerramos la base de datos
	}
	
	/**
	 * Implementación de los metodos definidos en InterficieMedia
	 */
	private final InterficieMedia.Stub mBinder = new InterficieMedia.Stub() {
		
		public void play(){
			start();
		}
		
		public void pause() throws DeadObjectException { 
				if(!mp.isPlaying()){
					mp.start();
				}
				else{
					mp.pause();
				}
		}
		
		public void stop() throws DeadObjectException { 
			mp.stop();
		}
		
		public void skipForward() throws RemoteException, IllegalArgumentException, SecurityException, IllegalStateException {
			try {
				nextSong();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		public void skipBack() throws RemoteException, IllegalArgumentException, SecurityException, IllegalStateException { 
			try {
				prevSong();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}


		public boolean isPlaying(){
			return mp.isPlaying();
		}

		public String getTitle() throws RemoteException {
			return actual.getTitol();
		}

		public int getTime() throws RemoteException {
			return mp.getCurrentPosition();
		}

		public int getDuration() throws RemoteException {
			return mp.getDuration();
		}
				
		public void playFile(String title) throws RemoteException, IllegalArgumentException, SecurityException, IllegalStateException{
			try {
				playSong(title,false);
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
			
		}
	};
	
	public IBinder onBind(Intent intent) {
		return mBinder;
	}
	
	/**
	 * Lanza la reproducción de temas "aleatorios".
	 * @throws IllegalArgumentException
	 * @throws SecurityException
	 * @throws IllegalStateException
	 */
	private void start() throws IllegalArgumentException, SecurityException, IllegalStateException{
		while(cancons.size() == 0){}
		actual = cancons.remove(0);
		back.add(0,actual);
		
		if(!db.existeixCanco(actual.getTitol())){
			db.insertarCanco(actual);
		}

		Uri url = Uri.parse(actual.getUrl());
		mp.reset();
		try {
			mp.setDataSource(MediaService.this, url);
			mp.prepare();
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		mp.start();
		startThread();
		
		
		mp.setOnCompletionListener(new OnCompletionListener() {
			public void onCompletion(MediaPlayer arg0) {
				try {
					nextSong();
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (SecurityException e) {
					e.printStackTrace();
				} catch (IllegalStateException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		});
		
	}
	
	/**
	 * Metodo que reproduce una cancion de la base de datos a partir del titulo, 
	 * si no tiene titulo escoge una cancion al azar que no esté vetada.
	 * @param title
	 * @param goback
	 * @throws UnsupportedEncodingException 
	 * @throws IllegalArgumentException
	 * @throws SecurityException
	 * @throws IllegalStateException
	 * @throws IOException
	 */
	public void playSong(String title, boolean goback) throws RemoteException, UnsupportedEncodingException {
		if(title == "None"){
			String artist = getRandomArtist();
			String song = getRandomSong(artist);
			while(db.isVetatS(song)){
				artist = getRandomArtist();
				song = getRandomSong(artist);
			}
			Cursor cActual = db.getCanco(song);
			cActual.moveToFirst();
			actual.setArtista(cActual.getString(2));
			actual.setTitol(song);
			actual.setCaratula(cActual.getString(4));
			actual.setPuntuacio(Integer.valueOf(cActual.getString(5)));
			cActual.close();
			
		}
		else{
			Cursor cActual = db.getCanco(title);
			cActual.moveToFirst();
			actual.setArtista(cActual.getString(2));
			actual.setTitol(title);
			actual.setCaratula(cActual.getString(4));
			actual.setPuntuacio(Integer.valueOf(cActual.getString(5)));
			cActual.close();
		}
		if(!goback){
			back.add(0,actual);
		}

		Goear g = new Goear(actual.getArtista(),actual.getTitol());
		g.searchSong();
		Uri url = Uri.parse(g.getMp3_url());
		
		mp.reset();
		try {
			mp.setDataSource(MediaService.this, url);
			mp.prepare();
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		mp.start();
		
		mp.setOnCompletionListener(new OnCompletionListener() {
			public void onCompletion(MediaPlayer arg0) {
				try {
					nextSong();
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (SecurityException e) {
					e.printStackTrace();
				} catch (IllegalStateException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			
		});
	}
	
	
	/**
	 * Metodo para reproducir la siguiente cancion.
	 * @throws IllegalArgumentException
	 * @throws SecurityException
	 * @throws IllegalStateException
	 * @throws IOException
	 */
	private void nextSong () throws IllegalArgumentException, SecurityException, IllegalStateException, IOException {
		start();
	}
	
	/**
	 * Metodo para reproducir la cancion anterior, si la lista "back" no esta vacia
	 * elimina el primer elemento y lo reproduce.
	 * @throws IllegalArgumentException
	 * @throws SecurityException
	 * @throws IllegalStateException
	 * @throws IOException
	 * @throws RemoteException 
	 */
	private void prevSong() throws IllegalArgumentException, SecurityException, IllegalStateException, IOException, RemoteException{
		if(!back.isEmpty()){
			cancons.add(0,actual);
			actual = back.remove(0);
			playSong(actual.getTitol(),true);
		}
	}
	
	/**
	 * Busca una cancion en el top de canciones del artista indicado de forma
	 * aleatoria. Devuelve un objeto tipo Canco con toda la informacion del tema.
	 * @return Canco
	 * @throws UnsupportedEncodingException
	 */
	public Canco getNewCanco() throws UnsupportedEncodingException{
		String rArtist = getRandomArtist(); //obtenemos un artista disponible de la BD

		RelatedArtistParser ra = new RelatedArtistParser(rArtist); //obtenemos la lista de artistas relacionados
		List<Artista> a = ra.parse();
		
		/* Si la lista tiene mas de 4 elementos, se reduce a 1/4 para obtener
		 * artistas mas parecidos posibles.
		 */
		if(a.size() > 4) 
			randint = random.nextInt((a.size() / 4) - 1);
		else
		    randint = random.nextInt(a.size()-1);

		Artista artist = a.get(randint);	//escogemos uno al azar
		while(true){
			if(db.existeixArtista(artist.getNom())){ //si existe en la bd y no esta vetado acabamos
				if(!db.isVetat(artist.getNom())){
					break;
				}
			}
			else{
				if(a.size() > 4)
					randint = random.nextInt(a.size() / 4);
				else
					randint = random.nextInt(a.size());
				
				artist = a.get(randint);
				if(!db.existeixArtista(artist.getNom())) //Si no existe en la BD acabamos
					break;
			}
		}
		
		TopSongsParser ts = new TopSongsParser(artist.getNom()); //obtenemos el top de canciones del artista
		
		List<Canco> c = ts.parse();
		randint = random.nextInt(c.size()-1);
		
		Canco canco = c.get(randint);
		
		while(true){
			if(db.existeixCanco(canco.getTitol())){ //Si esta en BD y no esta vetada acabamos
				if(!db.isVetatS(canco.getTitol())){
					break;
				}
				else{
					randint = random.nextInt(c.size()-1);
					canco = c.get(randint);
				}
			}else{//Si no existe acabamos
				break;
			}
		}
		canco.setArtista(artist.getNom());
		
		if(canco.getCaratula() == "caratula"){ 
			canco.setCaratula(artist.getImgUrl());
		}
		
		Goear g = new Goear(canco.getArtista(), canco.getTitol());
		
		if(g.searchSong()) //Si la cancion esta en goear, seteamos la Url
			canco.setUrl(g.getMp3_url());
		else
			canco.setUrl("N/A"); //Sino la marcamos con un "N/A"
		
		return canco;
	}
	
	/**
	 * Retorna el nombre de un artista aleatorio a partir
	 * de los artistas no vetados de la base de datos.
	 * @return String
	 */
	private String getRandomArtist(){
		Cursor artists = db.getAvaibleArtists();
		artists.moveToFirst();
		
		randint = random.nextInt(artists.getCount());
        artists.moveToPosition(randint);
        
        String rartist = artists.getString(0);
        artists.close();

        
        return rartist;
	}
	
	/**
	 * Retorna una cancion aleatoria de la base de datos del artista indicado.
	 * @param artist
	 * @return String
	 */
	private String getRandomSong(String artist){
		Cursor songs = db.getCancons(artist);
		songs.moveToFirst();
		
		randint = random.nextInt(songs.getCount());
		songs.moveToPosition(randint);
		
		String randsong = songs.getString(0);
		songs.close();
		
		return randsong;
	}
	
	
	/**
	 * Metodo para lanzar el thread "runner"
	 */
	public void startThread(){
		  if(runner == null){
		    runner = new Thread(this);
		    runner.start();
		  }
	}
	
	/**
	 * Metodo para finalizar el thread "runner"
	 */
	public void stopThread(){
		  if(runner != null){
		    Thread moribund = runner;
		    runner = null;
		    moribund.interrupt();
		  }
	}
	
	
	/**
	 * Thread que llena una lista con las canciones en cola, a partir de
	 * artistas escogidos aleatoriamente de la base de datos.
	 */
	public void run() {
		while(true){
			if(cancons.size() < 2){
				try {
			        Canco c = getNewCanco();
			        if(c.getUrl() != "N/A"){
			        	cancons.add(c);
			        }
					
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace();
				}
				
			}
			else{
				break;
			}
		}
		stopThread(); //cuando la lista esta llena, el thread se termina
	}
}




	